package test.java;

import homes.java.CapturasInfractores;
import homes.java.CategoriasInfractores;
import homes.java.VehiculosInfractores;

import java.util.HashSet;
import junit.framework.Assert;

import negocio.java.Captura;
import negocio.java.Categoria;
import negocio.java.Detector;
import negocio.java.Evasor;
import negocio.java.Frecuente;
import negocio.java.Imagen;
import negocio.java.Infraccion;
import negocio.java.Inhabilitado;
import negocio.java.Vehiculo;
import negocio.java.Zona;

import org.junit.Before;
import org.junit.Test;

import mocks.java.MockAnalizadorFail;
import mocks.java.MockAnalizadorSuccess;
import mocks.java.MockMailSender;
import mocks.java.MockSmsSender;


public class testVehiculos {

	private Imagen imagen;
	private Zona zona;
	private Infraccion infraccion;
	private Detector detectorConFallas;
	private Detector detectorExitoso;
	private Captura captura;
	private Captura captura1;
	private Captura captura2;
	private Captura captura3;
	private Captura captura4;
	private Captura captura5;
	private MockSmsSender mockSmsSender;
	private MockMailSender mockMailSender;
	private VehiculosInfractores vehiculosInfractores;
	private CapturasInfractores capturasInfractores;
	private CategoriasInfractores categoriasInfractores;
	
    @Before
	public void initialize(){
		this.zona = new Zona("5555-5555");
		this.imagen = new Imagen();
		this.infraccion = new Infraccion("Mal Estacionado", 10, 10);
		
		this.captura = new Captura(zona, imagen, infraccion);
		this.captura1 = new Captura(zona, imagen, infraccion);
		this.captura2 = new Captura(zona, imagen, infraccion);
		this.captura3 = new Captura(zona, imagen, infraccion);
		this.captura4 = new Captura(zona, imagen, infraccion);
		this.captura5 = new Captura(zona, imagen, infraccion);
		
		VehiculosInfractores.reset();
		CapturasInfractores.reset();
		CategoriasInfractores.reset();
		
		this.vehiculosInfractores = VehiculosInfractores.getInstance();
		this.capturasInfractores = CapturasInfractores.getInstance();
		this.categoriasInfractores = CategoriasInfractores.getInstance();
		
		this.vehiculosInfractores.addInfractor(new Vehiculo("AAA000"));
		
		this.mockSmsSender = new MockSmsSender();
		this.mockMailSender = new MockMailSender();
		
		this.categoriasInfractores.addCategoria(new Evasor(this.mockMailSender));
		this.categoriasInfractores.addCategoria(new Inhabilitado(this.mockSmsSender));
		this.categoriasInfractores.addCategoria(new Frecuente(this.mockMailSender, this.mockSmsSender));
		
		this.detectorConFallas = new Detector(new MockAnalizadorFail(), categoriasInfractores, vehiculosInfractores, capturasInfractores);
		this.detectorExitoso = new Detector(new MockAnalizadorSuccess(), categoriasInfractores, vehiculosInfractores, capturasInfractores);
	} 

	/* 1) Ingresar una nueva infracciÃ³n al sistema y verificar que al vehÃ­culo en cuestiÃ³n se le realizan las penalizaciones
	 *  correspondientes al tipo de infracciÃ³n: quita de puntos e incremento de lo adeudado por multas
	 */
	@Test
	public void ingresarInfraccionYPenalizar(){
		this.detectorExitoso.tomarCaptura(this.captura);     
		       
		Vehiculo vehiculo = this.captura.getVehiculo();  
		Assert.assertEquals(Vehiculo.PUNTOSINICIALES - this.infraccion.getPuntos() ,vehiculo.getPuntosRegistro());
		Assert.assertEquals(Vehiculo.IMPORTEINICIAL + this.infraccion.getCosto() ,vehiculo.getImporteMultas());
	}
	
	private <T> boolean contieneCategoria(HashSet<Categoria> categorias, T categoria){
    	for (Categoria cat : categorias){
    		if (categoria == cat.getClass())
    			return true;
    	}
    	return false;
    }
	
	/* 2) Dado un vehÃ­culo que se encuentra al lÃ­mite de ser incorporado a una categorÃ­a, 
	 * ingresarle una nueva infracciÃ³n y verificar que se le agregue Ì�la categorÃ­a correspondiente
	 *  (esto debe ser verificado para las tres categorÃ­as) */
	@Test
	public void AgregarCategoriaFrecuente(){	   
		this.agregaTodasLasCapturas(this.detectorExitoso);
		
		Assert.assertTrue(this.captura.getVehiculo().getInfracciones().size() > 5); //valido que haya puesto mas de 5 infracciones		
		Assert.assertTrue(this.contieneCategoria(this.captura.getVehiculo().getCategorias(), Frecuente.class)); 
	}
	
	private void agregaTodasLasCapturas(Detector detector){    			
		detector.tomarCaptura(this.captura1);
		detector.tomarCaptura(this.captura2);
		detector.tomarCaptura(this.captura3);
		detector.tomarCaptura(this.captura4);
		detector.tomarCaptura(this.captura5); 
		detector.tomarCaptura(this.captura);
	}
	
	@Test
	public void AgregarCategoriaEvasor(){
		Infraccion infraccion = new Infraccion("Sin sinturon de Seguridad", 900.0 , 80);//le sumo $900 de multa
		captura.setInfraccion(infraccion);
		this.detectorExitoso.tomarCaptura(this.captura);
				
		Infraccion infraccion2 = new Infraccion("Papeles que no estan al dia", 100.10, 1); //le sumo $100.10 y es evasor
		captura2.setInfraccion(infraccion2);
		this.detectorExitoso.tomarCaptura(this.captura2);
		
       
        Assert.assertTrue(this.captura.getVehiculo().getImporteMultas() >= 1000); //valido que el importe que debe sea mayor o igual a 1000
        Assert.assertTrue(this.contieneCategoria(this.captura.getVehiculo().getCategorias(), Evasor.class));
	}
	
	@Test
	public void AgregarCategoriaInhabilitado(){
		Infraccion infraccion = new Infraccion("Pasar semÃ¡foro en rojo", 90.0 , 999);//le resto 999 puntos
		captura.setInfraccion(infraccion);
		this.detectorExitoso.tomarCaptura(this.captura);
				
		Infraccion infraccion2 = new Infraccion ("Exceso de velocidad", 243.0, 1); //le resto 1 mas y esta inhabilitado
		captura2.setInfraccion(infraccion2);
		this.detectorExitoso.tomarCaptura(this.captura2);
		
		
		Assert.assertTrue(this.captura.getVehiculo().getPuntosRegistro() == 0);
		Assert.assertTrue(this.contieneCategoria(this.captura.getVehiculo().getCategorias(), Inhabilitado.class));
    }
	
	/* 3) Ingresar una infracciÃ³Ì�n y verificar que se envÃ­an las notificaciones correspondientes.*/
    @Test
	public void IngresoInfraccionYVerificoNotificacionesEvasor(){
    	Assert.assertEquals(0, this.mockMailSender.getMailsEnviados().size());
    	
    	Infraccion infraccion = new Infraccion("Mal Estacionado", 1500, 500);
    	captura.setInfraccion(infraccion);
    	this.detectorExitoso.tomarCaptura(this.captura);
		
		Assert.assertEquals(1, this.mockMailSender.getMailsEnviados().size());
	}
    
    @Test
	public void IngresoInfraccionYVerificoNotificacionesInhabilitado(){
    	Assert.assertEquals(0, this.mockSmsSender.getNumerosEnviados().size());
    	
    	Infraccion infraccion = new Infraccion("Mal Estacionado", 500, 1500);
    	captura.setInfraccion(infraccion);
    	this.detectorExitoso.tomarCaptura(this.captura);
		
		Assert.assertEquals(1, this.mockSmsSender.getNumerosEnviados().size());
	}
    
    @Test
	public void IngresoInfraccionYVerificoNotificacionesFrecuente(){
    	Assert.assertEquals(0, this.mockSmsSender.getNumerosEnviados().size());
    	Assert.assertEquals(0, this.mockMailSender.getMailsEnviados().size());
   
    	this.infraccion.agregarMail("mail@gmail.com");
    	this.infraccion.agregarMail("mail2@gmail.com");
    	this.infraccion.agregarNroCel("15-5555-5555");
    	this.infraccion.agregarNroCel("15-5555-5556");
    	this.captura.setInfraccion(this.infraccion);
    	this.agregaTodasLasCapturas(this.detectorExitoso);    	
    	
		Assert.assertEquals(2, this.mockSmsSender.getNumerosEnviados().size());
		Assert.assertEquals(2, this.mockMailSender.getMailsEnviados().size());
	}
	
	/* 4) Ingresar una infraccion de forma que su foto no puede ser identificada por el sistema externo reconocedor de
	 patentes y verificar que fue agregada a la lista de pendientes. */
	@Test
	public void capturaDeFotoConPatenteIrreconocible(){				
		Assert.assertTrue(this.capturasInfractores.getAllPendientes().size() == 0);
		this.detectorConFallas.tomarCaptura(this.captura);
		Assert.assertTrue(this.capturasInfractores.getAllPendientes().size() == 1);
	}
	
	/* 5) Asignar una patente manualmente a una infracciÃƒÂ³n que no pudo ser reconocida por el sistema externo y
	verificar que se respeta el mismo comportamiento que en los casos automÃƒÂ¡ticos (quita de puntos, actualizaciÃƒÂ³n
	de deuda, agregado a nuevas categorÃƒÂ­a envÃƒÂ­o de notificaciones).*/
	@Test   
	public void ingresoPatenteManual(){
		Assert.assertEquals(0, this.mockSmsSender.getNumerosEnviados().size());
	
		Infraccion infraccion = new Infraccion("Conducir en estado de ebriedad", 900, Vehiculo.PUNTOSINICIALES);
		
		/* EL detector con fallas nunca reconoce patentes */
		this.captura.setInfraccion(infraccion);
		this.detectorConFallas.tomarCaptura(this.captura);		
		this.detectorConFallas.ingresarPatenteManual(this.captura , "AAA000");
		
		/* Obtengo el vehiculo de la captura actual (captura a la cual recien ingrese la patente) */
		Vehiculo vehiculo = this.captura.getVehiculo();
			
		Assert.assertEquals(Vehiculo.PUNTOSINICIALES - infraccion.getPuntos() ,vehiculo.getPuntosRegistro());
		Assert.assertEquals(Vehiculo.IMPORTEINICIAL + infraccion.getCosto() ,vehiculo.getImporteMultas());
		Assert.assertEquals(1, this.mockSmsSender.getNumerosEnviados().size());
	}	

}
